﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace GameMath
{
    public struct GameVector2F
    {

        public GameVector2F(float magnitude, float direction) : this()
        {
            this.Magnitude = magnitude;
            this.Direction = direction;
        }

        public GameVector2F(GamePointF tail, GamePointF head) : this()
        {
            Magnitude = (float)Math.Sqrt(Math.Pow(tail.X + head.X, 2) + Math.Pow(tail.Y + head.Y, 2));

            double cosDirection = (tail.X + head.X) / Magnitude;
            double radian = Math.Acos(cosDirection);
            double deg = radian * 180 / Math.PI;

            Direction = new GameDegree((float)deg);
        }

        public float Magnitude { get; private set; }
        public GameDegree Direction { get; private set; }

        /// <summary>
        /// Adds the two vectors v1 and v2.
        /// </summary>
        /// <param name="v1">The first vector.</param>
        /// <param name="v2">The second vector.</param>
        /// <returns></returns>
        public static GameVector2F operator +(GameVector2F v1, GameVector2F v2)
        {
            //pythagoras
            float magnitude = (float)Math.Sqrt(Math.Pow((v1.Magnitude * Math.Cos((double)v1.Direction)) + (v2.Magnitude * Math.Cos(v2.Direction)), 2) + Math.Pow((v1.Magnitude * Math.Sin(v1.Direction)) + (v2.Magnitude * Math.Sin(v2.Direction)), 2));

            //cosinus relation
            float direction = (float)Math.Acos((Math.Pow(v2.Magnitude, 2) / 2 * magnitude * v1.Magnitude - Math.Pow(magnitude, 2) - Math.Pow(v2.Magnitude, 2)));

            return new GameVector2F(magnitude, direction);
        }

        /// <summary>
        /// Subtracts v2 from v1.
        /// </summary>
        /// <param name="v1">The first vector.</param>
        /// <param name="v2">The second vector.</param>
        /// <returns></returns>
        public static GameVector2F operator -(GameVector2F v1, GameVector2F v2)
        {
            return v1 + new GameVector2F(v2.Magnitude, v2.Direction + 180);
        }

        /// <summary>
        /// Multiplies v1 with a scalar value. This will only affect the magnitude of the vector.
        /// </summary>
        /// <param name="v1">The vector to be multiplied with the scalar value.</param>
        /// <param name="scalar">The scalar value to be multiplied with the vector.</param>
        /// <returns></returns>
        public static GameVector2F operator *(GameVector2F v1, float scalar)
        {
            return new GameVector2F(v1.Magnitude * scalar, v1.Direction);
        }
    }

    public struct GamePointF
    {
        public GamePointF(float x, float y) : this()
        {
            X = x;
            Y = y;
        }

        public float X {get;private set;}
        public float Y {get;private set;}

        public static explicit operator GamePointF(System.Drawing.Point p)
        {
            return new GamePointF(p.X, p.Y);
        }

        public static GamePointF operator *(GamePointF p, GameVector2F v)
        {
            double cosDir = Math.Cos( v.Direction.ToRadian());
            double sinDir = Math.Sin(v.Direction.ToRadian());
            float x = (float)(cosDir * v.Magnitude);
            float y = (float)(sinDir * v.Magnitude);

            return new GamePointF(p.X + x, p.Y + y);

        }

        public System.Drawing.Point ToPoint()
        {
            return new System.Drawing.Point((int)X, (int)Y);
        }

        public override string ToString()
        {
            return String.Format("({0}, {1})", X, Y);
        }
    }

    /// <summary>
    /// Values from 0 to 359.
    /// </summary>
    public struct GameDegree
    {
        public GameDegree(float value) : this()
        {
            //modulo always return positive!
            Value = 360 - value % 360;
        }

        public float Value { get; private set; }

        public float ToRadian()
        {
            return (float)(Value * (Math.PI / 180));
        }

        public static GameDegree operator +(GameDegree d1, GameDegree d2)
        {
            return new GameDegree(d1.Value + d2.Value);
        }

        public static GameDegree operator -(GameDegree d1, GameDegree d2)
        {
            return new GameDegree(d1.Value - d2.Value);
        }

        public static implicit operator double(GameDegree deg)
        {
            return (double)deg.Value;
        }

        public static implicit operator float(GameDegree deg)
        {
            return (float)deg.Value;
        }

        public static implicit operator GameDegree(float f)
        {
            return new GameDegree(f);
        }

        public override string ToString()
        {
            return String.Format("{0}", Value);
        }
    }
}
